﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Linq;

namespace Gmantis.Controls
{
    public static class ContextMenuService
    {
        // Fields
        internal static readonly DependencyProperty ContextMenuObjectProperty = DependencyProperty.RegisterAttached("ContextMenuObject", typeof(IContextMenu), typeof(ContextMenuService), null);
        public static readonly DependencyProperty ContextMenuProperty = DependencyProperty.RegisterAttached("ContextMenu", typeof(IContextMenu), typeof(ContextMenuService), new PropertyMetadata(null, new PropertyChangedCallback(ContextMenuService.OnContextMenuPropertyChanged)));
        internal static readonly DependencyProperty MouseHelperProperty = DependencyProperty.RegisterAttached("MouseHelper", typeof(MouseHelper), typeof(ContextMenuService), null);

        // Methods
        private static void _mouseHelper_ContextMenuInvoked(object sender, MouseEventArgs e)
        {
            FrameworkElement relativeTo = (FrameworkElement)sender;
            Point position = ((e != null) && (relativeTo != null)) ? e.GetPosition(relativeTo) : new Point();
            ShowContextMenu(relativeTo, position);
        }

        public static IContextMenu GetContextMenu(DependencyObject element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            return (IContextMenu)element.GetValue(ContextMenuProperty);
        }

        private static void InsertElementIntoVisualTree(object sender, System.Windows.RoutedEventArgs e, FrameworkElement menu)
        {
            FrameworkElement reference = menu;
            if (VisualTreeHelper.GetParent(reference) == null)
            {
                FrameworkElement elem = sender as FrameworkElement;
                if (elem != null)
                {
                    Panel panel = elem as Panel;
                    if (panel == null)
                    {
                        panel = elem.GetAllChildren<Panel>().FirstOrDefault<Panel>();
                    }
                    if (panel == null)
                    {
                        panel = elem.GetAllParents().FirstOrDefault<FrameworkElement>(fe => (fe is Panel)) as Panel;
                    }
                    if (panel != null)
                    {
                        try
                        {
                            panel.Children.Add(reference);
                        }
                        catch
                        {
                            panel = null;
                        }
                    }
                }
            }
        }

        private static void OnContextMenuPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UIElement owner = (UIElement)d;
            IContextMenu newValue = e.NewValue as IContextMenu;
            if (e.OldValue != null)
            {
                UnregisterContextMenu(owner);
            }
            if (newValue != null)
            {
                RegisterContextMenu(owner, newValue);
            }
        }

        private static void RegisterContextMenu(UIElement owner, IContextMenu menu)
        {
            RoutedEventHandler handler = null;
            MouseHelper helper = new MouseHelper(owner);
            helper.ContextMenuInvoked += new MouseEventHandler(ContextMenuService._mouseHelper_ContextMenuInvoked);
            owner.SetValue(MouseHelperProperty, helper);
            owner.SetValue(ContextMenuObjectProperty, menu);
            FrameworkElement element = owner as FrameworkElement;
            if ((element != null) && (menu != null))
            {
                menu.PlacementTarget = element;
                if (handler == null)
                {
                    handler = (s, e) => InsertElementIntoVisualTree(s, e, (FrameworkElement)menu);
                }
                element.Loaded += handler;
            }
        }

        public static void SetContextMenu(DependencyObject element, IContextMenu value)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            element.SetValue(ContextMenuProperty, value);
        }

        private static void ShowContextMenu(FrameworkElement element, Point position)
        {
            if (((element != null) && (VisualTreeHelper.GetParent(element) != null)) && element.IsInVisualTree())
            {
                IContextMenu menu = element.GetValue(ContextMenuObjectProperty) as IContextMenu;
                if (menu != null)
                {
                    menu.Show(element, position);
                }
            }
        }

        private static void UnregisterContextMenu(UIElement owner)
        {
            if (owner.GetValue(ContextMenuObjectProperty) != null)
            {
                MouseHelper helper = owner.GetValue(MouseHelperProperty) as MouseHelper;
                helper.ContextMenuInvoked -= new MouseEventHandler(ContextMenuService._mouseHelper_ContextMenuInvoked);
            }
        }
    }
}
